using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using LS.UnityExtensions;

namespace LS.Digitize
{
    public abstract class Reader : IReader, IDisposable
    {
        /// <summary>The settings used to create this reader.</summary>
        // public ES3Settings settings;
        protected int serializationDepth = 0;

        public abstract int ReadInt();
        public abstract float ReadFloat();
        public abstract bool ReadBool();
        public abstract char ReadChar();
        public abstract decimal ReadDecimal();
        public abstract double ReadDouble();
        public abstract long ReadLong();
        public abstract ulong ReadUlong();
        public abstract byte ReadByte();
        public abstract sbyte ReadSbyte();
        public abstract short ReadShort();
        public abstract ushort ReadUshort();
        public abstract uint ReadUint();
        public abstract string ReadString();
        public abstract byte[] ReadByteArray();
        public abstract string ReadPropertyName();
        public abstract byte[] ReadElement();
        public abstract void SkipElement();
        public abstract void Dispose();
        public abstract bool StartReadFile();
        public abstract void EndReadFile();
        public abstract bool CouldBeNull();
        public abstract void ReadNull();
        public virtual bool Goto(string key)
        {
            if (key == null)
                throw new ArgumentNullException("Key cannot be NULL when loading data.");

            string currentKey;
            while ((currentKey = ReadPropertyName()) != key)
            {
                if (currentKey == null)
                    return false;
                SkipElement();
            }
            return true;
        }

        public virtual string GoNext()
        {
            string currentKey = ReadPropertyName();
            return currentKey;
        }
        
        public virtual bool StartReadObject()
        {
            serializationDepth++;
            return false;
        }
        
        public virtual void EndReadObject()
        {
            serializationDepth--;
        }
        public abstract bool StartReadDictionary();
        public abstract void EndReadDictionary();
        public abstract bool StartReadDictionaryKey();
        public abstract void EndReadDictionaryKey();
        public abstract bool StartReadDictionaryValue();
        public abstract void EndReadDictionaryValue();
        public abstract bool StartReadCollection();
        public abstract void EndReadCollection();
        public abstract bool StartReadCollectionItem();
        public abstract void EndReadCollectionItem();

        public Reader()
        {
        }

        // If this is not null, the next call to the Properties will return this name.
        internal string overridePropertiesName = null;

        /// <summary>Allows you to enumerate over each field name. This should only be used within an ES3Type file.</summary>
        public virtual ReaderPropertyEnumerator Properties
        {
            get { return new ReaderPropertyEnumerator(this); }
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public class ReaderPropertyEnumerator
        {
            public Reader reader;

            public ReaderPropertyEnumerator(Reader reader)
            {
                this.reader = reader;
            }

            public IEnumerator GetEnumerator()
            {
                string propertyName;
                while (true)
                {
                    // Allows us to repeat a property name or insert one of our own.
                    if (reader.overridePropertiesName != null)
                    {
                        string tempName = reader.overridePropertiesName;
                        reader.overridePropertiesName = null;
                        yield return tempName;
                    }
                    else
                    {
                        if ((propertyName = reader.ReadPropertyName()) == null)
                            yield break;
                        yield return propertyName;
                    }
                }
            }
        }
    }
}